package com.qf.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.qf.feign.OrderServiceFeign;
import com.qf.pojo.Order;
import com.qf.pojo.R;
import com.qf.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.print.attribute.standard.PrinterURI;
import java.util.List;

@RestController
@RequestMapping("/user")
@RefreshScope  //监听配置中心配置的变化，一旦配置文件发生变化，会对这个类的实例对象进行重建
public class UserController {

//    @Autowired
//    UserService userService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient client;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private OrderServiceFeign orderServiceFeign;

    @Value("${desc}")
    private String desc;

    /**
     *  blockHandler 对应的方法必须和目标方法同参同返回值，这类方法参数列表中最后一个参数必须是BlockException
     *      主要用来处理流控相关异常
     * @param id
     * @param b
     * @return
     */
    public R findOrdersBlockHandler(Integer id, BlockException b){
//        if (b instanceof FlowException){
//
//        }
//        b.printStackTrace();

        return R.error().putMsg("流控了....");
    }

    /**
     *  fallback 对应的方法必须和目标方法同参同返回值，参数列表最后一个可有可无，用来接收异常对象
     *      当出现了流控相关的异常，由blockHandler处理，其他异常都由fallback处理
     *      如果没有设置blockHandler，都由fallback处理，这个相当于是一个兜底方法
     * @param id
     * @param e
     * @return
     */
    public R findOrdersFallback(Integer id,Throwable e){
        e.printStackTrace();
        return R.error().putMsg("出异常了....");
    }

    /**
     *  defaultFallback 默认降级，这个降级方法空参的，但是返回值必须和目标方法一致。
     *      如果没有设置blockHandler或者fallback，就默认defaultFallback生效
     *      如果设置blockHandler或者fallback，没有defaultFallback什么事了。
     *
     * @param e
     * @return
     */
    public R defautFallback(Throwable e){
        e.printStackTrace();
        return R.error().putMsg("默认降级方法....");
    }

    @GetMapping("/{id}")
    @SentinelResource(value = "findOrders",
            blockHandler = "findOrdersBlockHandler",
            fallback = "findOrdersFallback",
            defaultFallback = "defautFallback"
    )
    public R findOrders(@PathVariable("id") Integer id) throws InterruptedException {

//        List<ServiceInstance> instances = client.getInstances("qf-order");
//        ServiceInstance serviceInstance = instances.get(0);

//        ServiceInstance serviceInstance = loadBalancerClient.choose("qf-order");
//        String host = serviceInstance.getHost();
//        int port = serviceInstance.getPort();
//        R r = restTemplate.getForObject("http://"+host+":"+port+"/order/" + id, R.class);

        //将LoadBalancer负载均衡和RestTemplate整合
        //R r = restTemplate.getForObject("http://qf-order/order/" + id, R.class);

        if (id == 1){
            //模拟慢调用
            //Thread.sleep(1000);
            //模拟异常比例&异常数
            throw new RuntimeException("error...");
        }

        R r = orderServiceFeign.findByUid(id);

        //r.putMsg(desc);

        return r;
    }

    @GetMapping("/test1")
    @SentinelResource("test1")
    public R test1(){
        return R.success().putMsg("test1访问成功");
    }

    @Autowired
    private UserService userService;

    @GetMapping("/test2")
    @SentinelResource("test2")
    public R test2(){

        userService.show();

        return R.success().putMsg("test2访问成功");
    }

    @GetMapping("/test3")
    @SentinelResource("test3")
    public R test3(){
        userService.show();
        return R.success().putMsg("test3访问成功");
    }

}
